<?php

use App\Game\Conf\GameProgress;
use App\Game\Conf\MainCmd;
use App\Game\Conf\SubCmd;
use App\Game\Core\Packet;
use App\Model\User;
use App\Service\Hupai;
use App\Service\PaiXing;
use App\Service\TaskCenterService;
use App\Service\FreeOrderTaskService;
use Swoole\Websocket\Frame;
use Hyperf\Server\ServerFactory;
use Hyperf\Utils\ApplicationContext;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\DbConnection\Db;

if (!function_exists('container')) {
    function container()
    {
        return ApplicationContext::getContainer();
    }
}
if (!function_exists('redis')) {
    function redis()
    {
        return container()->get(\Redis::class);
    }
}
if (!function_exists('server')) {
    function server()
    {
        return container()->get(ServerFactory::class)->getServer()->getServer();
    }
}
if (!function_exists('frame')) {
    function frame()
    {
        return container()->get(Frame::class);
    }
}
if (!function_exists('websocket')) {
    function websocket()
    {
        return container()->get(WebSocketServer::class);
    }
}

if (!function_exists('rand_str')) {
    function rand_str($len = 10, $special = false)
    {
        $chars = array(
            "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
            "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
            "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
            "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
            "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
            "3", "4", "5", "6", "7", "8", "9"
        );
        if ($special) {
            $chars = array_merge($chars, array(
                "!", "@", "#", "$", "?", "|", "{", "/", ":", ";",
                "%", "^", "&", "*", "(", ")", "-", "_", "[", "]",
                "}", "<", ">", "~", "+", "=", ",", "."
            ));
        }
        $charsLen = count($chars) - 1;
        shuffle($chars);                            //打乱数组顺序
        $str = '';
        for ($i = 0; $i < $len; $i++) {
            $str .= $chars[mt_rand(0, $charsLen)];    //随机取出一位
        }
        return $str;
    }
}
if (!function_exists('mj_sub_str')) {
    function mj_sub_str($content, $start = 0, $end = 0)
    {
        return mb_substr($content, $start, $end, 'utf-8');
    }
}

if (!function_exists('roll_prize')) {
    function roll_prize($proArr)
    {
        $new_proArr = array_column($proArr, 'chance_per', 'id');
        $result = '';
        //概率数组的总概率精度
        $proSum = array_sum($new_proArr);
        //概率数组循环
        foreach ($new_proArr as $key => $proCur) {
            $randNum = mt_rand(1, $proSum);
            if ($randNum < $proCur) {
                $result = $key;
                break;
            } else {
                $proSum -= $proCur;
            }
        }
        unset ($new_proArr);
        return $result;
    }
}

function dd($name, $data)
{
    echo "\r\n";
    print_r('+++++++++++++++++++++++ ' . $name . ' | ' . date('Y-m-d H:i:s', time()) . ' +++++++++++++++++++++++++ ');
    echo "\r\n";
    print_r($data);
    echo "\r\n";
    echo "\r\n";
    echo "\r\n";
}

function ddNode($num)
{
    echo "\r\n";
    echo "\r\n";
    print_r($num);
    echo ' | ' . date('Y-m-d H:i:s', time()) . "\r\n";
    echo "\r\n";
    echo "\r\n";
}

function ddTitle($title)
{
    echo "\r\n";
    echo "\r\n";
    print_r('--------------------------------- ' . $title . ' | ' . date('Y-m-d H:i:s', time()) . '--------------------------------- ');
    echo "\r\n";
    echo "\r\n";
}

function SUCC($data = [])
{
    return [
        'code' => 1,
        'msg' => 'success',
        'data' => $data
    ];
}

function ERROR(string $msg = 'error', int $code = 0)
{
    return [
        'code' => $code,
        'msg' => $msg,
        'data' => []
    ];
}

function object_array($array)
{
    if (is_object($array)) {
        $array = (array)$array;
    }
    if (is_array($array)) {
        foreach ($array as $key => $value) {
            $array[$key] = object_array($value);
        }
    }
    return $array;
}

function array_group_by($arr, $key)
{
    $grouped = [];
    foreach ($arr as $value) {
        $grouped[$value[$key]][] = $value;
    }
    if (func_num_args() > 2) {
        $args = func_get_args();
        foreach ($grouped as $key => $value) {
            $parms = array_merge([$value], array_slice($args, 2, func_num_args()));
            $grouped[$key] = call_user_func_array('array_group_by', $parms);
        }
    }
    return $grouped;
}

function expire_today()
{
    $time_now = time();
    $time_str = date('Y-m-d', time()) . ' 23:59:59';
    $today_end = strtotime($time_str);
    $expire = bcsub($today_end, $time_now);
    return $expire;
}


function addTimer($room_no, $timerId, $name, $time)
{
//    $roomData = getRoomData($room_no);
//    $timerArr = isset($roomData['timer']) ? json_decode($roomData['timer'], true) : [];
    $timerArr = ['exec' => $name, 'timer_id' => $timerId, 'time' => date('Y-m-d H:i:s', $time)];
    ddTitle('add ' . $name . ' timer : ' . $timerId . ' | ' . $room_no);
    updateRoomData($room_no, ['timer' => $timerArr]);
}


function getLastTimer($room_no)
{
    $room_data = getRoomData($room_no);
    if (empty($room_data)) {
        return 0;
    }
    $timerArr = isset($room_data['timer']) ? json_decode($room_data['timer'], true) : [];
    if (empty($timerArr)) {
        return 0;
    }
    return $timerArr;
}

function cleanTimer($room_no)
{
//    $user_conf = config('user');
//    $user_room_timer_lock_key = sprintf($user_conf['user_room_timer_lock'], $room_no);   // 锁键
//    // 加锁
//    $result = redis()->set($user_room_timer_lock_key, $room_no, ['nx', 'ex' => 3]);
//    //nx代表当key不存在时设置 ex代表设置过期时间
//    if (!$result) {
//
//    }


    $room_data = getRoomData($room_no);
    if (empty($room_data)) {
        return;
    }

    $timerArr = isset($room_data['timer']) ? json_decode($room_data['timer'], true) : [];
    if (empty($timerArr)) {
        return;
    }
    $timerID = $timerArr['timer_id'];
    dd('timer-info', \Swoole\Timer::info($timerID));
    $res = \Swoole\Timer::clear((int)$timerID);
    ddTitle('cleanTimer : res : ' . $res . ' | id : ' . $timerID . ' result:' . ($res ? 1 : 0));
//    updateRoomData($room_no, ['timer' => []]);
}

function putTimerAdd($timer, $room_no, $wait_time)
{
    $game_conf = config('game');
    $timerArr = json_encode(['timer' => $timer, 'room_no' => $room_no, 'wait_time' => $wait_time]);
//    dd('timerArr', $timerArr);
    $room_no -= 10000;
    if ($room_no % 2 == 0) {//偶数进slot2
        $addkey = $game_conf['user_room_timer_add_two'];
    } else {//奇数进slot1
        $addkey = $game_conf['user_room_timer_add'];
    }
    redis()->lPush($addkey, $timerArr);
}

function putTimerDel($room_no)
{
    $game_conf = config('game');
    $room_data = getRoomData($room_no);
    if (empty($room_data)) {
        return;
    }
    $timerArr = isset($room_data['timer']) ? json_decode($room_data['timer'], true) : [];
    if (empty($timerArr)) {
        return;
    }
    $timerId = $timerArr['timer_id'];
    $exec = $timerArr['exec'];
    $room_no -= 10000;
    if ($room_no % 2 == 0) {//偶数进slot2
        $delkey = $game_conf['user_room_timer_del_two'];
    } else {//奇数进slot1
        $delkey = $game_conf['user_room_timer_del'];
    }

    $data = json_encode(['exec' => $exec, 'id' => $timerId]);
    redis()->lPush($delkey, $data);
}

function getCardColor($card)
{
    if (in_array($card, [1, 2, 3, 4, 5, 6, 7, 8, 9])) {
        return 1;
    }
    if (in_array($card, [11, 12, 13, 14, 15, 16, 17, 18, 19])) {
        return 2;
    }
    if (in_array($card, [21, 22, 23, 24, 25, 26, 27, 28, 29])) {
        return 3;
    }
    return 0;
}

function addRoomRobot($room_no, $account, $score = 0)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_robot'], $room_no);
    redis()->hSet($key, (string)$account, $score);
}

function getRoomAllRobot($room_no)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_robot'], $room_no);
    if (redis()->exists($key)) {
        return redis()->hGetAll($key);
    }
    return [];
}

function is_robot($room_no, $account)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_robot'], $room_no);
    if (redis()->hExists($key, (string)$account)) {
        return true;
    }
    return false;
}

function update_robot_score($room_no, $account, $score)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_robot'], $room_no);
    $oldScore = 0;
    if (redis()->exists($key)) {
        $res = redis()->hGet($key, (string)$account);
        if ($res) {
            $oldScore = intval($res);
        }
    }
    $newScore = $oldScore + $score;
    redis()->hSet($key, (string)$account, $newScore);
}

function removeRoomRobot($room_no)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_robot'], $room_no);
    redis()->del($key);
}

function addOperCounter($room_no)
{
    $game_conf = config('game');
    $room_counter_key = sprintf($game_conf['user_room_counter'], $room_no);
    if (redis()->exists($room_counter_key)) {
        redis()->incr($room_counter_key);
    } else {
        redis()->setex($room_counter_key, 120, 1);
    }
}


function getCounter($room_no)
{
    $game_conf = config('game');
    $room_counter_key = sprintf($game_conf['user_room_counter'], $room_no);
    $counter = 0;
    if (redis()->exists($room_counter_key)) {
        $counter = redis()->get($room_counter_key);
    }
    return $counter;
}

function resetOperCounter($room_no)
{
    $game_conf = config('game');
    $room_counter_key = sprintf($game_conf['user_room_counter'], $room_no);
    redis()->setex($room_counter_key, 120, 0);
}

function delOperCounter($room_no)
{
    $game_conf = config('game');
    $room_counter_key = sprintf($game_conf['user_room_counter'], $room_no);
    redis()->del($room_counter_key);
}

function checkCounter($room_no)
{
    $game_conf = config('game');
    $room_counter_key = sprintf($game_conf['user_room_counter'], $room_no);
    $counter = 0;
    if (redis()->exists($room_counter_key)) {
        $counter = redis()->get($room_counter_key);
    } else {
        redis()->setex($room_counter_key, 120, 0);
    }
    if ($counter > 1) {
        return false;
    }
    return true;
}

function checkGameProgress($room_no, $gameProgress)
{
    $progress = getGameProgress($room_no);
    if (is_array($gameProgress)) {
        if (!in_array($progress, $gameProgress)) {
            return false;
        }
    } else {
        if ($progress != $gameProgress) {
            return false;
        }
    }
    return true;
}


function setGameProgress($room_no, int $gameProgress)
{
    $game_conf = config('game');
    $room_progress_key = sprintf($game_conf['user_room_progress'], $room_no);
    $type = getGameProgressType($room_no);
    $data = ['num' => $gameProgress, 'type' => $type];
    redis()->set($room_progress_key, json_encode($data));
}

function getGameProgress($room_no)
{
    $game_conf = config('game');
    $room_progress_key = sprintf($game_conf['user_room_progress'], $room_no);
    if (redis()->exists($room_progress_key)) {
        $res = json_decode(redis()->get($room_progress_key), true);
        return $res['num'];
    }
    return 0;
}

function setGameProgressType($room_no, int $type = 0)
{
    $game_conf = config('game');
    $room_progress_key = sprintf($game_conf['user_room_progress'], $room_no);
    $gameProgress = getGameProgress($room_no);
    $data = ['num' => $gameProgress, 'type' => $type];
    redis()->set($room_progress_key, json_encode($data));
}

function getGameProgressType($room_no)
{
    $game_conf = config('game');
    $room_progress_key = sprintf($game_conf['user_room_progress'], $room_no);
    if (redis()->exists($room_progress_key)) {
        $res = json_decode(redis()->get($room_progress_key), true);
        return $res['type'];
    }
    return 0;

}


function resetGameProgress($room_no)
{
    $game_conf = config('game');
    $room_progress_key = sprintf($game_conf['user_room_progress'], $room_no);
    redis()->del($room_progress_key);
}

//清除游戏准备数据
function resetGamePrepare($room_no)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    redis()->del($room_prepare_key);
}

//清除单个玩家游戏准备数据
function removeGamePrepareByAccount($room_no, $account)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    redis()->hDel($room_prepare_key, (string)$account);
}

//清除单个玩家游戏数据
function removeRoomUserDataByAccount($room_no, $account)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_data'], $room_no);
    redis()->hDel($key, (string)$account);
}

//玩家准备
function playerGamePrepare($room_no, $account)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    redis()->hSet($room_prepare_key, (string)$account, GameProgress::USER_ONLINE);
}

//玩家准备
function getGamePrepare($room_no, $account)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    $res = redis()->hget($room_prepare_key, (string)$account);
    $prepare = 0;
    if ($res) {
        $prepare = $res;
    }
    return $prepare;
}

//获取全部准备信息
function getGamePrepareALL($room_no)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    $res = redis()->hGetAll($room_prepare_key);
    if (!$res) {
        return [];
    }
    return $res;
}

//玩家退出
function playerQuit($room_no, $account)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    redis()->hDel($room_prepare_key, (string)$account);

}

//玩家断线
function playerOffline($room_no, $account)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    redis()->hSet($room_prepare_key, (string)$account, GameProgress::USER_OFFLINE);
}

//玩家上线
function playerOnline($room_no, $account)
{
    ddTitle('game pull  reset playerOnline = 1 | room_no:' . $room_no . ' | account:' . $account);
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    redis()->hSet($room_prepare_key, (string)$account, GameProgress::USER_ONLINE);
}

//获取玩家准备状态
function playerStatus($room_no, $account)
{
    $game_conf = config('game');
    $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
    $user_status = redis()->hGet($room_prepare_key, (string)$account);
    if (!$user_status) {
        return 0;
    }
    return $user_status;
}


/**
 * 获取房间其他用户的fds
 * @param $room_no
 * @param $account
 * @return array
 */
function getOtherFdsByRoomNo($room_no)
{
    $game_conf = config('game');
    $room_wait_key = sprintf($game_conf['user_room_wait'], $room_no);
    $room_users = redis()->get($room_wait_key);
    $room_users = $room_users ? json_decode($room_users, true) : [];
    $fds = [];
    if (!$room_users) {
        return $fds;
    }
    foreach ($room_users as $k => $v) {
        $bind_key = sprintf($game_conf['user_bind_key'], $v);
        $fds[] = redis()->get($bind_key);
    }
    return $fds;
}

function getCheckHPGArr($room_no)
{
    $game_conf = config('game');
    $room_check_hpg_key = sprintf($game_conf['user_room_checkhpg'], $room_no);
    if (redis()->exists($room_check_hpg_key)) {
        return redis()->hGetAll($room_check_hpg_key);
    }
    return [];
}

function addCheckHpgArr($room_no, $account, $type)
{
    $game_conf = config('game');
    $room_check_hpg_key = sprintf($game_conf['user_room_checkhpg'], $room_no);
    redis()->hSet($room_check_hpg_key, (string)$account, $type);
}

function resetCheckHpg($room_no)
{
    $game_conf = config('game');
    $room_check_hpg_key = sprintf($game_conf['user_room_checkhpg'], $room_no);
    redis()->del($room_check_hpg_key);
}

function addCheckHpg($room_no, $account, $type)
{
    $game_conf = config('game');
    $room_check_hpg_key = sprintf($game_conf['user_room_checkhpg'], $room_no);
    redis()->lPush($room_check_hpg_key, $account . ',' . $type);
}


/**
 * 把php数组存入redis的hash表中
 * @param $arr
 * @param $hash_key
 */
function arrToHashInRedis(array $arr, string $hash_key)
{
    foreach ($arr as $key => $val) {
        $value = $val;
        if (is_array($val)) {
            $value = json_encode($val, true);
        }
        redis()->hSet($hash_key, (string)$key, $value);
    }
}

/**
 * 批量更新房间数据
 * @param $room_no
 * @param $params
 */
function updateRoomData($room_no, $params)
{
    $game_conf = config('game');
    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
    if (redis()->exists($room_data_key)) {
        foreach ($params as $key => $val) {
            $value = $val;
            if (is_array($val)) {
                $value = json_encode($val, true);
            }
            redis()->hSet($room_data_key, (string)$key, $value);
        }
    }
}

/**
 * 批量更新房间数据
 * @param $room_no
 * @param $params
 */
function resetRoomData($room_no, $params = [])
{

    $game_conf = config('game');
    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
    if (empty($params)) {
        redis()->del($room_data_key);
        return;
    }
    if (redis()->exists($room_data_key)) {
        redis()->del($room_data_key);
        foreach ($params as $key => $val) {
            $value = $val;
            if (is_array($val)) {
                $value = json_encode($val, true);
            }
            redis()->hSet($room_data_key, (string)$key, $value);
        }
    }
}


/**
 * 设置我的手牌
 * @param $user_room_data
 * @param $cards
 * @param $account
 * @return mixed
 */
function setMyOutCard($room_no, $out_cards, $account, $round, $hand_num, $last_chair_id, $next_chair_id)
{
    $room_data = getRoomData($room_no);
    //根据椅子查询手牌信息
    $my_card = json_decode($room_data[$account], true);

    //判断总手牌数，是否存在重复出牌，杠牌也按3张牌算，这样可以保证总牌数不变是13，判断总牌数就更简单
    $totalCardNum = count($my_card['card']) + count($my_card['peng']) * 3 + count($my_card['gang']) * 3;
    if ($totalCardNum < 14) {
        $msg = array('status' => 3, 'info' => '出牌非法，重复出牌');
        $serv = server();
        $fd = getFdByAccount($account);
        pushErr($serv, $fd, SubCmd::SUB_GAME_OUT_CARD_RESP, $msg);
        return;
    }
    foreach ($my_card['card'] as $k => $v) {
        if ($v == $out_cards['card']) {
            unset($my_card['card'][$k]);
            break;
        }
    }
    sort($my_card['card']);
    $out_card = [];
    if (isset($my_card['out_card'])) {
        $out_card = $my_card['out_card'];
    }
    $out_card[] = $out_cards['card'];
    $my_card['out_card'] = $out_card;
    $param[$account] = $my_card;
    $param['round'] = $round;
    $param['hand_num'] = $hand_num;
    $param['last_chair_id'] = $last_chair_id;
    $param['next_chair_id'] = $next_chair_id;
    $param['out'] = $out_cards;
//    dd('out_cards update param', $param);
    updateRoomData($room_no, $param);
    return $param;
}

/**
 * 设置我的手牌
 * @param $user_room_data
 * @param $cards
 * @param $account
 * @return mixed
 */
function setMyGangCard($user_room_data, $card, $last_chair_id, $next_chair_id, $account, $zimo = false)
{
    $out_chair_id = $last_chair_id;
    //根据椅子查询手牌信息
    $my_card = json_decode($user_room_data[$account], true);
    dd('gang card befor', $my_card['card']);
    $my_card['card'] = array_diff($my_card['card'], [$card]);
    dd('gang card after', $my_card['card']);
    $score = PaiXing::$point * 2;//默认2倍
    $reward = '刮风';
    if ($zimo) {
        $out_chair_id = $next_chair_id;
        if (in_array($card, $my_card['peng'])) { //补杠一倍
            $my_card['peng'] = array_diff($my_card['peng'], [$card]);
            $score = PaiXing::$point;
        } else {
            $reward = '下雨';
        }
    }
    $my_card['hu_detail'][] = ['reward' => $reward, 'bonus' => $score, 'type' => 2, 'from' => $out_chair_id];  //1=胡牌 2=杠牌
    $my_card['gang'][] = $card;
    sort($my_card['card']);
    $param[$account] = $my_card;
    $param['last_chair_id'] = $last_chair_id;
    $param['next_chair_id'] = $next_chair_id;
    $room_no = $user_room_data['room_no'];
    updateRoomData($room_no, $param);
    $piaoArr = setScore($room_no, $score, $account, $last_chair_id, $zimo);
    return $piaoArr;
}

/**
 * 设置我的手牌
 * @param $user_room_data
 * @param $cards
 * @param $account
 * @return mixed
 */
function setMyPengCard($user_room_data, $card, $chair_id, $account)
{
    //根据椅子查询手牌信息
    $my_card = json_decode($user_room_data[$account], true);
//    dd('peng card befor', $my_card['card']);
    $my_card['card'] = array_diff($my_card['card'], [$card]);
//    dd('peng card after', $my_card['card']);
    $my_card['peng'][] = $card;
    sort($my_card['card']);
    $param[$account] = $my_card;
    //下一个出牌人椅子id
    $param['next_chair_id'] = $chair_id;
    updateRoomData($user_room_data['room_no'], $param);
    return $my_card;
}


//随机换牌（默认推荐同花色最少的3张）
function randChangeCard(array $cards)
{
    $res = array_count_values($cards);
    $wan = 0;
    $tong = 0;
    $tiao = 0;
    $wanArr = [];
    $tongArr = [];
    $tiaoArr = [];
    foreach ($res as $card => $num) {
        if (in_array($card, [1, 2, 3, 4, 5, 6, 7, 8, 9])) {
            $wan += $num;
            for ($i = 0; $i < $num; $i++) {
                $wanArr[] = $card;
            }
        }
        if (in_array($card, [11, 12, 13, 14, 15, 16, 17, 18, 19])) {
            $tong += $num;
            for ($i = 0; $i < $num; $i++) {
                $tongArr[] = $card;
            }
        }
        if (in_array($card, [21, 22, 23, 24, 25, 26, 27, 28, 29])) {
            $tiao += $num;
            for ($i = 0; $i < $num; $i++) {
                $tiaoArr[] = $card;
            }
        }
    }
    $arr = ['1' => $wan, '2' => $tong, '3' => $tiao];
//    dd('arr', $arr);
    $tem = $wan > 2 ? $wan : ($tong > 2 ? $tong : $tiao);
    $index = '';
    foreach ($arr as $k => $v) {
        if (($v <= $tem) && ($v >= 3)) {
            $tem = $v;
            $index = $k;
        }
    }
//    dd('tem', $tem);
//    dd('index', $index);
    $temArr = [];
    if ($index == 1) {
        $temArr = $wanArr;
    }
    if ($index == 2) {
        $temArr = $tongArr;
    }
    if ($index == 3) {
        $temArr = $tiaoArr;
    }
//    dd('temArr', $temArr);
    $cardArr = [];
    $num = 1;
    foreach ($temArr as $v) {
        if ($num > 3) {
            break;
        }
        $cardArr[] = $v;
        $num++;
    }
    return $cardArr;
}


//随机定缺，默认推荐牌最少的花色
function randColor($room_no, $account)
{
    $room_data = getRoomData($room_no);
    $user_data = json_decode($room_data[$account], true);
    $res = array_count_values($user_data['card']);
    $wan = 0;
    $tong = 0;
    $tiao = 0;
    foreach ($res as $card => $num) {
        if (in_array($card, [1, 2, 3, 4, 5, 6, 7, 8, 9])) {
            $wan += $num;
        }
        if (in_array($card, [11, 12, 13, 14, 15, 16, 17, 18, 19])) {
            $tong += $num;
        }
        if (in_array($card, [21, 22, 23, 24, 25, 26, 27, 28, 29])) {
            $tiao += $num;
        }
    }
    $arr = ['1' => $wan, '2' => $tong, '3' => $tiao];
    $tem = $wan ? $wan : ($tong ? $tong : $tiao);
    $index = '';
    foreach ($arr as $k => $v) {
        if ($v <= $tem) {
            $tem = $v;
            $index = $k;
        }
    }
    return $index;
}

function getRoomChangeCards($room_no)
{
    $game_conf = config('game');
    $room_change_key = sprintf($game_conf['user_room_change'], $room_no);
    if (redis()->exists($room_change_key)) {
        return json_decode(redis()->get($room_change_key), true);
    } else {
        return [];
    }
}

function getUserColorByChairId($room_no, $chair_id)
{
    $colors = getRoomColors($room_no);
    return $colors[$chair_id];
}

function getUserColorByAccount($room_no, $account)
{
    $colors = getRoomColors($room_no);
    $chair_id = getChairIdbyAccount($room_no, $account);
    return $colors[$chair_id];
}

function resetRoomChangeCards($room_no)
{
    $game_conf = config('game');
    $room_change_key = sprintf($game_conf['user_room_change'], $room_no);
    redis()->del($room_change_key);
}

function getRoomColors($room_no)
{
    $game_conf = config('game');
    $room_color_key = sprintf($game_conf['user_room_color'], $room_no);
    if (redis()->exists($room_color_key)) {
        $res = json_decode(redis()->get($room_color_key), true);
        return $res;
    } else {
        return [];
    }
}

function setRoomColors($room_no, $user_colors)
{
    $game_conf = config('game');
    $room_color_key = sprintf($game_conf['user_room_color'], $room_no);
    redis()->set($room_color_key, json_encode($user_colors));
}

function resetRoomColors($room_no)
{
    $game_conf = config('game');
    $room_change_key = sprintf($game_conf['user_room_color'], $room_no);
    redis()->del($room_change_key);
}

function getRoomChairs($room_no)
{
    $game_conf = config('game');
    $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
    if (redis()->exists($room_chair_key)) {
        return json_decode(redis()->get($room_chair_key), true);
    } else {
        return [];
    }
}

function resetRoomChairs($room_no)
{
    $game_conf = config('game');
    $room_change_key = sprintf($game_conf['user_room_chair'], $room_no);
    redis()->del($room_change_key);
}


function getChairIdbyAccount($room_no, $account)
{
    $chairs = getRoomChairs($room_no);
    if (empty($chairs)) {
        return 0;
    }
    foreach ($chairs as $k => $v) {
        if ($k == $account) {
            return $v;
        }
    }
}

function getUserInfoByFd($fd)
{
    $game_conf = config('game');
    $user_info_key = sprintf($game_conf['user_info_key'], $fd);
    $uinfo = redis()->get($user_info_key);
    if ($uinfo) {
        return json_decode($uinfo, true);
    }
    return [];
}

function getUserInfoByAcc($account)
{
    $fd = getFdByAccount($account);
    return getUserInfoByFd($fd);
}

function getRoomData($room_no)
{
    $game_conf = config('game');
    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
    if (redis()->exists($room_data_key)) {
        return redis()->hGetAll($room_data_key);;
    } else {
        return [];
    }
}

function clearRoomData($room_no)
{
    $game_conf = config('game');
    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
    redis()->del($room_data_key);
}

function clearRoomChair($room_no)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_chair'], $room_no);
    redis()->del($key);
}

function getRoomWait($room_no)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_wait'], $room_no);
    if (redis()->exists($key)) {
        return json_decode(redis()->get($key), true);
    }
    return [];
}

function setRoomWaitByArr($room_no, $arr)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_wait'], $room_no);
    redis()->set($key, json_encode($arr));
}

function setTakeOverByAccount($room_no, $account)
{
    $room_data = getRoomData($room_no);
    $user_data = json_decode($room_data[$account], true);
    $user_data['take_over'] = 1;
    updateRoomData($room_no, [$account => $user_data]);
}


function removeRoomWaitByAccount($room_no, $account)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_wait'], $room_no);
    if (redis()->exists($key)) {
        $res = json_decode(redis()->get($key), true);
        foreach ($res as $k => $acc) {
            if ($acc == $account) {
                unset($res[$k]);
            }
        }
        if (empty($res)) {
            redis()->del($key);
        } else {
            redis()->set($key, json_encode($res));
        }
    }
}


function clearRoomWait($room_no)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_wait'], $room_no);
    redis()->del($key);
}

function removeRoomChairByAccount($room_no, $account)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_chair'], $room_no);
    if (redis()->exists($key)) {
        $res = json_decode(redis()->get($key), true);
        foreach ($res as $acc => $v) {
            if ($acc == $account) {
                unset($res[$acc]);
            }
        }
        if (empty($res)) {
            redis()->del($key);
        } else {
            redis()->set($key, json_encode($res));
        }
    }
}

function clearRoomMap($room_no)
{
    $game_conf = config('game');
    $users = getRoomChairs($room_no);
    if (count($users)) {
        foreach ($users as $account => $chair_id) {
            $key = sprintf($game_conf['user_room'], $account);
            redis()->del($key);
        }
    }

}

function removeRoomTotalByRoomNo($room_no)
{
    $game_conf = config('game');
    $key = $game_conf['user_room_total'];
    redis()->sRem($key, $room_no); // 清除总房间数里的当前房间
}


function clearRoom($room_no)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room_chair'], $room_no);
    redis()->del($key);
}


function clearRoomDataAll($room_no)
{
    putTimerDel($room_no); //清除定时器
    delOperCounter($room_no);//清除游戏计数器
    clearRoomMap($room_no);
    clearRoomWait($room_no);
    resetCheckHpg($room_no);
    resetRoomChangeCards($room_no);
    resetRoomColors($room_no);
    resetGamePrepare($room_no);
    resetGameProgress($room_no);
    clearRoomChair($room_no);
    clearRoomData($room_no);
    removeRoomRobot($room_no);
    removeRoomTotalByRoomNo($room_no);

}

function isGameOver($room_no)
{
    $room_data = getRoomData($room_no);
    if (empty($room_data)) {
        return 1;
    }
    return $room_data['is_game_over'];
}

function getFdByAccount($account)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_bind_key'], $account);
    if (redis()->exists($key)) {
        return redis()->get($key);;
    } else {
        return 0;
    }
}

function getAccountByFd($fd)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_info_key'], $fd);
    if (redis()->exists($key)) {
        $uinfo = json_decode(redis()->get($key), true);
        return $uinfo['account'];
    }
    return 0;
}

function getAccountByChairId($room_no, $chairId)
{
    $account = 0;
    $chairs = getRoomChairs($room_no);
    foreach ($chairs as $uid => $chair_id) {
        if ($chairId == $chair_id) {
            $account = $uid;
        }
    }
    return $account;
}

/**
 * 返回玩家房间桌位号
 * @param $room_no
 * @param $account
 * @return mixed
 */
function findChairId($room_no, $account)
{
    $game_conf = config('game');
    $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
    $chair_data = json_decode(redis()->get($room_chair_key), true);
    return $chair_data[$account];
}


/**
 * 设置我的手牌
 * @param $user_room_data
 * @param $cards
 * @param $account
 * @return mixed
 */
function setMyCard($user_room_data, $cards, $account, $round, $hand_num, $last_chair_id, $next_chair_id)
{
    //根据椅子查询手牌信息
    $my_card = json_decode($user_room_data[$account], true);
    if (gettype($my_card) == 'string') {
        $my_card = json_decode($my_card, true);
    }
    foreach ($my_card['card'] as $k => $v) {
        if ($v == $cards['card']) {
            unset($my_card['card'][$k]);
            break;
        }
    }
    sort($my_card['card']);
    $out_card = [];
    if (isset($my_card['out_card'])) {
        $out_card = $my_card['out_card'];
    }
    $out_card[] = $cards['card'];
    $my_card['out_card'] = $out_card;
    $param[$account] = $my_card;
    $param['round'] = $round;
    $param['hand_num'] = $hand_num;
    $param['last_chair_id'] = $last_chair_id;
    $param['next_chair_id'] = $next_chair_id;
    updateRoomData($user_room_data['room_no'], $param);
    return $my_card;
}


/**
 * 设置积分
 * @param $user_room_data
 * @param $cards
 * @param $account
 * @return mixed
 */
function setScore($room_no, $score, $account, $from_chair_id, $zimo = false)
{
    $user_room_data = getRoomData($room_no);
    //根据椅子查询手牌信息
    $uinfo_arr = json_decode($user_room_data['uinfo'], true);
    $winscore = $zimo ? $score * 3 : $score; //如果自摸则收三家的分
    $uinfo_arr[$account]['score'] += $winscore;
    $uinfo_arr[$account]['total_score'] += $winscore;
    $piaoArr[] = ['chair_id' => $uinfo_arr[$account]['chair_id'], 'score' => $winscore];
    //{"5":{"rate":1,"score":1},"6":{"rate":1,"score":0},"7":{"rate":1,"score":0},"8":{"rate":1,"score":0}}
    $from_account = getAccountByChairId($room_no, $from_chair_id);
    if ($zimo) {
        foreach ($uinfo_arr as $k => $v) {
            if ($k != $account) {
                $uinfo_arr[$k]['score'] -= $score;
                $uinfo_arr[$k]['total_score'] -= $score;
                $piaoArr[] = ['chair_id' => $uinfo_arr[$k]['chair_id'], 'score' => (int)-$score];
            }
        }
    } else {
        $piaoArr[] = ['chair_id' => $uinfo_arr[$from_account]['chair_id'], 'score' => (int)-$score];
        $uinfo_arr[$from_account]['score'] -= $score;
        $uinfo_arr[$from_account]['total_score'] -= $score;
    }
    $param['uinfo'] = $uinfo_arr;
    updateRoomData($room_no, $param);
    return $piaoArr;
}

//发牌
function SendCard(int $room_no, int $account, $chair_id, $is_gang = false)
{
    $user_room_data = getRoomData($room_no);
    $hand = json_decode($user_room_data['hand'], true);
    //底牌剩余数为0，则游戏结束，流程不继续往下执行
    if (count($hand) == 0) {
        return ['status' => 1, 'info' => '游戏已结束'];
    }
    //底牌拿出一张牌
    $arr = array_splice($hand, 0, 1);
    $moCard = $arr[0];
    $my_card = json_decode($user_room_data[$account], true);

    //判断总手牌数，是否存在重复出牌，杠牌也按3张牌算，这样可以保证总牌数不变是13，判断总牌数就更简单
    $totalCardNum = count($my_card['card']) + count($my_card['peng']) * 3 + count($my_card['gang']) * 3;
    if ($totalCardNum != 13) {
        $msg = array('status' => 2, 'info' => '非法操作，重复发牌', 'total_card_num' => $totalCardNum);
        $serv = server();
        $fd = getFdByAccount($account);
        pushErr($serv, $fd, SubCmd::SUB_GAME_SEND_CARD_RESP, $msg);
        return ['status' => 2, 'info' => '非法操作，重复发牌 | chair_id: ' . $chair_id . ' | mycard: '
            . $user_room_data[$account]];
    }

//    $totalCardNum = count($my_card['card']) + count($my_card['peng']) * 3 + count($my_card['gang']) * 3;
//    $totalGangNum = count($my_card['gang']);
//    if ($totalCardNum != 13) {
//        $flag = false;
//        switch ($totalGangNum) {
//            case 1:
//                $flag = $totalCardNum == 14 ? true : false;
//                break;
//            case 2:
//                $flag = $totalCardNum == 15 ? true : false;
//                break;
//            case 3:
//                $flag = $totalCardNum == 16 ? true : false;
//                break;
//            case 4:
//                $flag = $totalCardNum == 17 ? true : false;
//                break;
//        }
//        ddTitle("totalCardNum: $totalCardNum, totalGangNum: $totalGangNum, flag: $flag");
//        if (!$flag) {
//            return ['status' => 2, 'info' => '非法操作，重复发牌 | chair_id: ' . $chair_id . ' | mycard: '
//                . $user_room_data[$account]];
//        }
//    }

    $my_card['card'][] = $moCard;

    sort($my_card['card']);
    $param[$account] = $my_card;
    $param['hand'] = $hand;
    if (count($hand) == 0) {
        $param['is_game_over'] = 1;
    }
    $param['in'] = ['card' => $moCard, 'chair_id' => $chair_id, 'is_gang' => $is_gang ? 1 : 0];
    $param['next_chair_id'] = $chair_id;
    //写会redis
    updateRoomData($room_no, $param);

    return ['status' => 0, 'data' => ['card' => $moCard, 'chair_id' => $chair_id, 'hand_num' => count($hand)]];

}

function checkHu($room_no, $out_cards, $account, $handNum, $zimo = false, $beforGang = false)
{
    $user_room_data = getRoomData($room_no);
    $user_conf = config('user');
    $card = $out_cards['card'];
    $out_chair_id = $out_cards['chair_id'];
    $user_data = json_decode($user_room_data[$account], true);
    $my_card = $user_data['card'];
    $cards = $my_card;
    if (!$zimo) { //非自摸的情况，需要把打出牌加入手牌进行胡牌检查
        $cards[] = $card;
    }
    $peng = $user_data['peng'];
    $gang = $user_data['gang'];
    $colors = getRoomColors($room_no);
    $chair_id = getChairIdbyAccount($room_no, $account);
    $colorNum = $colors[$chair_id];
    $checkNum = PaiXing::checkNum($cards, $peng, $gang);
    if (!$checkNum) {
        return ['status' => 1, 'info' => '胡牌失败，当前手牌数量有误！'];
    }
    //校验定缺，没有定缺花色的牌才可以胡牌
    $checkColor = PaiXing::checkColor($cards, $colorNum);
    if (!$checkColor) {
        return array('status' => 2, 'info' => '胡牌失败，当前手牌中有定缺牌！');
    }
    //计算胡牌
    $hu = (new Hupai())->start($cards, $peng, $gang);
    if (empty($hu)) {
        return array('status' => 3, 'info' => '炸胡');
    }
    //校验牌型
    $result = PaiXing::checkPaiXing($hu, $peng, $gang);
    if (!is_array($result)) {
        return array('status' => 4, 'info' => $result);
    }
    $chair_id = findChairId($room_no, $account); // 座位号
    $result['chair_id'] = $chair_id;
    $result['card'] = $card;
    $next_chair_id = $chair_id + 1;
    $next_chair_id = ($next_chair_id > 4) ? $next_chair_id - 4 : $next_chair_id;
    $result['next_chair_id'] = $next_chair_id;
    $result['wait_time'] = $user_conf['play_timer'];
    //本轮次上一次出牌椅子id
    $last_chair_id = $out_chair_id;
    $result['from_chair_id'] = $last_chair_id;

    $user_data['hu'][] = $card; //胡的那张牌加入缓存
    $user_data['hu'] = array_unique($user_data['hu']);


    $reward_name = $result['reward_name'];
    $other_name = [];
    $totalBonus = $result['bonus'];
    $totalScore = $result['score'];
    $from = $out_chair_id;
    //根数
    $genNum = $result['gen'];
    if ($genNum) {
        $genStr = '';
        switch ($genNum) {
            case 1 :
                $genStr = '一根';
                break;
            case 2 :
                $genStr = '二根';
                break;
            case 3 :
                $genStr = '三根';
                break;
            case 4 :
                $genStr = '四根';
                break;
        }
        $other_name[] = $genStr;
    }


    //自摸
    if ($zimo) {
        //---------------------------
        $other_name[] = '自摸';
        $totalBonus *= 2;
        $totalScore *= 2;
        $from = $chair_id;
        //------------------------------

        $result['zimo'] = 1;
//        $user_data['hu_detail'][] = [
//            'reward' => $result['reward_name'] . '(自摸)',
//            'bonus' => $result['bonus'] * 2,
//            'type' => 1, //1=胡牌 2=杠牌
//            'from' => $chair_id
//        ];
//        $result['score'] *= 2;

    } else {
//        $user_data['hu_detail'][] = [
//            'reward' => $result['reward_name'],
//            'bonus' => $result['bonus'],
//            'type' => 1, //1=胡牌 2=杠牌
//            'from' => $out_chair_id
//        ];
    }

    //如果打出的是最后一张牌
    if ($handNum == 0) {
        //------------------------------
        $other_name[] = $zimo ? '海底捞月' : '海底炮';
        $totalBonus *= 2;
        $totalScore *= 2;
        //------------------------------

//        $user_data['hu_detail'][] = [
//            'reward' => $zimo ? '海底捞月' : '海底炮',
//            'bonus' => 2,
//            'type' => 1, //1=胡牌 2=杠牌
//            'from' => $zimo ? $chair_id : $out_chair_id
//        ];
//        $result['score'] *= 2;
    }
    //抢杠胡
    if ($beforGang) {
        //------------------------------
        $other_name[] = '抢杠胡';
        $totalBonus *= 2;
        $totalScore *= 2;
        //------------------------------

//        $user_data['hu_detail'][] = [
//            'reward' => '抢杠胡',
//            'bonus' => 2,
//            'type' => 1,  //1=胡牌 2=杠牌
//            'from' => $out_chair_id
//        ];
//        $result['score'] *= 2;
    }


    //判断上次发牌是否是杠牌后的发牌
    $inCard = json_decode($user_room_data['in'], true);
    if ($inCard['is_gang']) { //杠牌后自摸

        //------------------------------
        $other_name[] = $zimo ? '杠上开花' : '杠上炮';
        $totalBonus *= 2;
        $totalScore *= 2;
        //------------------------------

//        $user_data['hu_detail'][] = [
//            'reward' => $zimo ? '杠上开花' : '杠上炮',
//            'bonus' => 2,
//            'type' => 1, //1=胡牌 2=杠牌
//            'from' => $zimo ? $chair_id : $out_chair_id
//        ];
//        $result['score'] *= 2;
        if (!$zimo) {//呼叫转移
            $pao_chair_id = $inCard['chair_id'];
            $pao_account = getAccountByChairId($room_no, $pao_chair_id);
            $pao_user_data = json_decode($user_room_data[$pao_account], true);
            $zyscore = 0; //转移杠牌积分
            foreach ($pao_user_data['hu_detail'] as $k => $v) {
                if ($v['type'] == 2) { //1=胡牌 2=杠牌
                    //暗杠的情况下要*3倍积分
                    $zyscore += $v['from'] == $pao_chair_id ? $v['bonus'] * PaiXing::$point * 3 : $v['bonus'] * PaiXing::$point;
                    //清掉点杠炮玩家的杠牌数据，并转移积分到胡牌玩家手中
                    unset($pao_user_data['hu_detail'][$k]);
                }
            }
            //------------------------------
            $other_name[] = '呼叫转移';
            $totalBonus += $zyscore;
            $totalScore += $zyscore;
            //------------------------------

//            $user_data['hu_detail'][] = [
//                'reward' => '呼叫转移',
//                'bonus' => $zyscore,
//                'type' => 1, //1=胡牌 2=杠牌
//                'from' => $out_chair_id
//            ];
//            $result['score'] += $zyscore;
            //扣除杠上炮玩家的积分
//            $uinfo_arr = json_decode($user_room_data['uinfo'], true);
//            $uinfo_arr[$pao_account]['score'] -= $zyscore;
//            //更新点杠炮玩家的积分和杠牌数据
//            $param['uinfo'] = $uinfo_arr;
//            $param[$pao_account] = $pao_user_data;
        }
    }

    if (!empty($other_name)) {
        $reward_name .= '(' . implode(',', $other_name) . ')';
    }

    $user_data['hu_detail'][] = [
        'reward' => $reward_name,
        'bonus' => $totalBonus,
        'type' => 1, //1=胡牌 2=杠牌
        'from' => $from
    ];

    if (!isset($user_room_data['next_master'])) {
        $next_master = isset($user_room_data['next_master']) ? $user_room_data['next_master'] :
            $chair_id;
        $param['next_master'] = $next_master;
    }

    $param['last_chair_id'] = $chair_id;
    $param['next_chair_id'] = $next_chair_id;

    if ($zimo) { //自摸的情况，需要把摸的牌从手牌中去除，防止下一圈的摸牌操作触发重复摸牌
        foreach ($user_data['card'] as $k => $v) {
            if ($v == $card) {
                unset($user_data['card'][$k]);
                break;
            }
        }
        sort($user_data['card']);
    }

    $param[$account] = $user_data;

//    dd('result-hu', $result);

//    dd('param', $param);

    updateRoomData($room_no, $param);

    //更新积分
    $piaoArr = setScore($room_no, $totalScore, $account, $result['from_chair_id'], $zimo);
    $result['piao'] = $piaoArr;
    return array('status' => 0, 'data' => $result);

}

function testHu($room_no, $out_cards, $account, $zimo = false)
{
    $user_room_data = getRoomData($room_no);
    $user_conf = config('user');
    $card = $out_cards['card'];
    $out_chair_id = $out_cards['chair_id'];
    $user_data = json_decode($user_room_data[$account], true);
    $my_card = $user_data['card'];
    $cards = $my_card;
    if (!$zimo) { //非自摸的情况，需要把打出牌加入手牌进行胡牌检查
        $cards[] = $card;
    }
    $peng = $user_data['peng'];
    $gang = $user_data['gang'];
    $colors = getRoomColors($room_no);
    $chair_id = getChairIdbyAccount($room_no, $account);
    $colorNum = $colors[$chair_id];
    $checkNum = PaiXing::checkNum($cards, $peng, $gang);
    if (!$checkNum) {
        return ['status' => 1, 'info' => '胡牌失败，当前手牌数量有误！'];
    }
    //校验定缺，没有定缺花色的牌才可以胡牌
    $checkColor = PaiXing::checkColor($cards, $colorNum);
    if (!$checkColor) {
        return array('status' => 2, 'info' => '胡牌失败，当前手牌中有定缺牌！');
    }
    //计算胡牌
    $hu = (new Hupai())->start($cards, $peng, $gang);
    if (empty($hu)) {
        return array('status' => 3, 'info' => '炸胡');
    }
    //校验牌型
    $result = PaiXing::checkPaiXing($hu, $peng, $gang);
    if (!is_array($result)) {
        return array('status' => 4, 'info' => $result);
    }
    $chair_id = findChairId($room_no, $account); // 座位号
    $result['chair_id'] = $chair_id;
    $result['card'] = $card;
    $next_chair_id = $chair_id + 1;
    $next_chair_id = ($next_chair_id > 4) ? $next_chair_id - 4 : $next_chair_id;
    $result['next_chair_id'] = $next_chair_id;
    $result['wait_time'] = $user_conf['play_timer'];
    //本轮次上一次出牌椅子id
    $last_chair_id = $out_chair_id;
    $result['from_chair_id'] = $last_chair_id;

    $user_data['hu'][] = $card; //胡的那张牌加入缓存
    $user_data['hu'] = array_unique($user_data['hu']);


    $reward_name = $result['reward_name'];
    $other_name = [];
    $totalBonus = $result['bonus'];
    $from = $out_chair_id;
    //根数
    $genNum = $result['gen'];
    if ($genNum) {
        $genStr = '';
        switch ($genNum) {
            case 1 :
                $genStr = '一根';
                break;
            case 2 :
                $genStr = '二根';
                break;
            case 3 :
                $genStr = '三根';
                break;
            case 4 :
                $genStr = '四根';
                break;
        }
        $other_name[] = $genStr;
    }


    //自摸
    if ($zimo) {
        //---------------------------
        $other_name[] = '自摸';
        $totalBonus *= 2;
        $from = $chair_id;
        //------------------------------

        $result['zimo'] = 1;

    }


    //判断上次发牌是否是杠牌后的发牌
    $inCard = json_decode($user_room_data['in'], true);
    if ($inCard['is_gang']) { //杠牌后自摸

        //------------------------------
        $other_name[] = $zimo ? '杠上开花' : '杠上炮';
        $totalBonus *= 2;
        //------------------------------
        if (!$zimo) {//呼叫转移
            $pao_chair_id = $inCard['chair_id'];
            $pao_account = getAccountByChairId($room_no, $pao_chair_id);
            $pao_user_data = json_decode($user_room_data[$pao_account], true);
            $zyscore = 0; //转移杠牌积分
            foreach ($pao_user_data['hu_detail'] as $k => $v) {
                if ($v['type'] == 2) { //1=胡牌 2=杠牌
                    //暗杠的情况下要*3倍积分
                    $zyscore += $v['from'] == $pao_chair_id ? $v['bonus'] * PaiXing::$point * 3 : $v['bonus'] * PaiXing::$point;
                    //清掉点杠炮玩家的杠牌数据，并转移积分到胡牌玩家手中
                    unset($pao_user_data['hu_detail'][$k]);
                }
            }
            //------------------------------
            $other_name[] = '呼叫转移';
            $totalBonus += $zyscore;

        }
    }

    if (!empty($other_name)) {
        $reward_name .= '(' . implode(',', $other_name) . ')';
    }

    $user_data['hu_detail'][] = [
        'reward' => $reward_name,
        'bonus' => $totalBonus,
        'type' => 1, //1=胡牌 2=杠牌
        'from' => $from
    ];

    return array('status' => 0, 'data' => $result);

}

function checkGang($user_room_data, $out_cards, $account, $zimo = false)
{
    $card = $out_cards['card'];
    $out_chair_id = $out_cards['chair_id'];
    $user_data = json_decode($user_room_data[$account], true);
    $my_card = $user_data['card'];
    $countValuesArr = array_count_values($my_card);
    if (!isset($countValuesArr[$card])) {
        return array('status' => 1, 'info' => '杠牌非法，该牌不存在');
    }
    //自摸
    if ($zimo) {

        if (in_array($card, $user_data['gang'])) {
            return array('status' => 2, 'info' => '杠牌非法，该牌已被杠过');
        }

        if (($countValuesArr[$card] != 4) && (!in_array($card, $user_data['peng']))) {
            return array('status' => 2, 'info' => '杠牌非法，手牌数据有问题');
        }
    } else {
        if ($countValuesArr[$card] != 3) {
            return array('status' => 4, 'info' => '杠牌非法，手牌数据有问题');
        }
        $pengArr = $user_data['peng'];
        if (in_array($card, $pengArr)) {
            return array('status' => 5, 'info' => '杠牌非法，该牌已碰');
        }
        $gangArr = $user_data['gang'];
        if (in_array($card, $gangArr)) {
            return array('status' => 6, 'info' => '杠牌非法，重复杠牌');
        }
    }
    $room_no = $user_room_data['room_no'];
    $chair_id = findChairId($room_no, $account); // 座位号
    //本轮次上一次出牌椅子id
    $outData = isset($user_room_data['out']) ? json_decode($user_room_data['out'], true) : [];
    if (!empty($outData)) {
        $last_chair_id = $outData['chair_id'];
    } else {
        $last_chair_id = isset($user_room_data['last_chair_id']) ? $user_room_data['last_chair_id'] : 0;
    }
    //下一个出牌人椅子id
    $next_chair_id = $chair_id;
    $piaoArr = setMyGangCard($user_room_data, $card, $last_chair_id, $next_chair_id, $account,
        $zimo);
    return array('status' => 0, 'data' => ['card' => $card, 'chair_id' => $chair_id, 'from_chair_id' => $last_chair_id, 'piao' => $piaoArr]);
}

function checkPeng($user_room_data, $out_cards, $account)
{
    $card = $out_cards;
    $user_data = json_decode($user_room_data[$account], true);
    $my_card = $user_data['card'];
    $countValuesArr = array_count_values($my_card);
    if (!isset($countValuesArr[$card])) {
        return array('status' => 1, 'info' => '碰牌非法，手牌数据有问题');
    }
    if ($countValuesArr[$card] != 2) {
        return array('status' => 2, 'info' => '碰牌非法，手牌数据有问题');
    }
    $pengArr = $user_data['peng'];
    if (in_array($card, $pengArr)) {
        return array('status' => 3, 'info' => '碰牌非法，手牌数据有问题');
    }
    $room_no = $user_room_data['room_no'];
    $chair_id = findChairId($room_no, $account); // 座位号
    $outData = isset($user_room_data['out']) ? json_decode($user_room_data['out'], true) : [];
    if (!empty($outData)) {
        $from_chair_id = $outData['chair_id'];
    } else {
        $from_chair_id = isset($user_room_data['last_chair_id']) ? $user_room_data['last_chair_id'] : 0;
    }
    setMyPengCard($user_room_data, $card, $chair_id, $account);
    return array('status' => 0, 'data' => ['card' => $card, 'chair_id' => $chair_id, 'from_chair_id' => $from_chair_id]);
}

function gameOver($room_no)
{
    $game_conf = config('game');
    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
    if (redis()->exists($room_data_key)) {
        $room_data = redis()->hGetAll($room_data_key);
        $uinfo_arr = json_decode($room_data['uinfo'], true);
        $returnData = [];
        $chairs = getRoomChairs($room_no);
        foreach ($chairs as $account => $chair_id) {
            // {"chair_id":1,"score":64,"nickname":"GGYY","total_bonus":16,"detial_list":[{"reward":"清一色","bonus":8},......]}
            $userData = json_decode($room_data[$account], true);
            $total_bonus = 0;
            $hu_detail = isset($userData['hu_detail']) ? $userData['hu_detail'] : [];
            if (!empty($hu_detail)) {
                $tem = [];
                foreach ($hu_detail as $v) {
                    $total_bonus += $v['bonus'];
                    $tem[] = $v;
                }
                $hu_detail = $tem;
            }
            $userInfo = \App\Model\User::userInfo($account);

            $returnData[] = [
                'chair_id' => $chair_id,
                'account' => $account,
                'score' => $uinfo_arr[$account]['score'],
                'nickname' => filterNickname($userInfo['nickname']),
                'total_bonus' => $total_bonus,
                'detial_list' => $hu_detail,
                'is_robot' => $userData['is_robot']
            ];
        }

        dd('gameOver returnData', $returnData);

        setGameProgress($room_no, GameProgress::GAME_CACULAR);

        //更新用户积分并记录流水
        $userArr = updateScore($room_no, $returnData);

        //更新每日任务
        TaskCenterService::updateMjDayTask($userArr);
        // 更新成长任务
        TaskCenterService::updateMjGrow($userArr);
        //更新免单任务
        FreeOrderTaskService::updateMjFreeOrderTask($userArr);

        if ($room_data['game_type'] == 1) {//大奖赛
            \App\Game\Core\Djs::roundOver($room_no, $returnData);
            return;
        }

        //全员发送结算通知
        broadCast($room_no, $returnData, SubCmd::SUB_GAME_CACULAR);

        //剔除所有机器人
        kickOutRobot($room_no);

        //剔除托管或掉线玩家
        kickOutTakerOverPlayers($room_no);

        $nextMaster = isset($room_data['next_master']) ? $room_data['next_master'] : 0;
        if (!$nextMaster) {
            $nextMaster = $room_data['master'] + 1;
            $nextMaster = $nextMaster > 4 ? $nextMaster - 4 : $nextMaster;
        }
        //重置对应游戏数据
        $new_room_data = [
            'master' => $nextMaster,
            'is_game_over' => 1,
            'room_no' => $room_no,
        ];

        resetRoomData($room_no, $new_room_data);

        //重置计数器
        delOperCounter($room_no);
        //清除定缺数据
        resetRoomColors($room_no);
        //清除座位数据
        resetRoomChairs($room_no);
        //清除换牌数据
        resetRoomChangeCards($room_no);
        //清除检查胡碰杠数据
        resetCheckHpg($room_no);
        //清除准备数据
        resetGamePrepare($room_no);
        //清除房间数据
        resetGameProgress($room_no);
        //清除定时器
        putTimerDel($room_no);
    }

}


function kickOutRobot($room_no)
{
    $robots = getRoomAllRobot($room_no);
    if (!empty($robots)) {
//        dd('robots', $robots);
        foreach ($robots as $account => $score) {
            usleep(500);
            $msg = ['status' => '退出房间', 'chair_id' => getChairIdbyAccount($room_no, $account)];
            broadCast($room_no, $msg, SubCmd::SUB_GAME_ROOM_QUIT_RESP);
            //清空机器人相关数据
            removeRoomWaitByAccount($room_no, $account);
            removeGamePrepareByAccount($room_no, $account);
            removeRoomChairByAccount($room_no, $account);
        }
        removeRoomRobot($room_no);
    }
}

function kickOutTakerOverPlayers($room_no)
{
    $waiters = getRoomWait($room_no);
//    dd('waiters', $waiters);
    foreach ($waiters as $account) {
        $user_data = getUserRoomDataByAccount($account);
//        dd('account', $account);
        $status = playerStatus($room_no, $account);
        $take_over = isset($user_data['take_over']) ? $user_data['take_over'] : 0;
        //托管或者掉线玩家直接踢出
        if ($status == GameProgress::USER_OFFLINE || $take_over) {
            sleep(4);
            pushForceQuitMsg($room_no, $account);
        }
    }
}

/**
 * 计算牌局积分
 * @param $winner
 * @param $user_score
 * @throws \Exception
 */
function updateScore($room_no, $cacularData)
{
    $logs = [];
    $updates = [];
    $create_at = time();
    $uidArr = [];

    $robots = getRoomAllRobot($room_no);
    foreach ($cacularData as $v) {
        if (!in_array($v['account'], $robots)) { //筛选出真人玩家
            $uidArr[] = $v['account'];
        }
    }
    $users = User::getUsers($uidArr);
    $uid_score = [];
    foreach ($users['data'] as $k => $v) {
        $uid_score[$v['account']] = $v['score'];
    }
    $returnData = [];
    foreach ($cacularData as $v) {

        if ($v['score'] == 0) { //0分不计入流水
            continue;
        }
        $score = isset($uid_score[$v['account']]) ? $uid_score[$v['account']] + $v['score'] : $v['score'];
        $is_win = $v['score'] > 0 ? 1 : 0;
        if (!$v['is_robot']) {
            $returnData[] = ['account' => $v['account'], 'is_win' => $is_win];
        }
        $updates[] = [
            'id' => $v['account'],
            'score' => $score ? $score : 0, //积分不够减的置为0
            'is_robot' => $v['is_robot'],
        ];

        $logs[] = [
            'user_id' => $v['account'],
            'is_robot' => $v['is_robot'],
            'score' => abs($v['score']),
            'before' => isset($uid_score[$v['account']]) ? $uid_score[$v['account']] : 0,
            'memo' => '麻将',
            'game_id' => 6,
            'createtime' => $create_at,
            'after' => $score,
            'type' => 7,// 7.麻将游戏、麻将游戏结算、麻将游戏积分变更 8.麻将游戏累计签到 9.麻将游戏签到
            'symbol' => $v['score'] > 0 ? '+' : '-'
        ];
    }

    //更新积分榜
    resetRankSort($logs);
    // 积分日志记录
    $log_arr = [];
    foreach ($logs as $k => $v) {
        if ($v['is_robot'] == 0) {
            unset($v['is_robot']);
            $log_arr[] = $v;
        }
    }
    //var_dump($log_arr,$update_data);
    // 开启事务
    Db::beginTransaction();
    try {
        Db::table('user_score_log')->insert($log_arr);
//        \App\Model\UserScoreLog::insertLog($log_arr);
    } catch (\Exception $e) {
        Db::rollBack();
    }
    foreach ($updates as $k => $v) {
        $is_limit = checkScoreLimit($v['id']);
        if (!$is_limit && ($v['is_robot'] == 0)) {
            try {
                Db::table('user')->where('id', $v['id'])->update(['score' => $v['score']]);
            } catch (\Throwable $ex) {
                Db::rollBack();
                break;
            }
        }
    }
    Db::commit();
    return $returnData;
}

function getRoomNoByAccount($account)
{
    $game_conf = config('game');
    $key = sprintf($game_conf['user_room'], $account);
    if (redis()->exists($key)) {
        return redis()->get($key);
    }
    return 0;
}

function getUserRoomDataByAccount($account)
{
    $game_conf = config('game');
    $room_no = getRoomNoByAccount($account);
    if (!$room_no) {
        return [];
    }
    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
    $user_data = redis()->hGet($room_data_key, (string)$account);
    if (!$user_data) {
        return [];
    }
    return json_decode($user_data, true);
}

/**
 * 检查用户每日积分超限
 * 检查用户每日积分超限
 * @param $account
 * @return bool
 */
function checkScoreLimit($account)
{
    $score_conf = config('score');
    $user_conf = config('user');
    $rank__day_key = $score_conf['score_rank_day'];
    if (redis()->exists($rank__day_key)) {
        $score = redis()->zScore($rank__day_key, (string)$account);
        if ($score && ($score >= $user_conf['max_score_day'])) {
            return true;
        }
    }
    return false;
}

function pushDataToUser($serv, $fd, $data, $scmd, $code = 1)
{
    $data = Packet::packFormat('OK', 1, $data);
    $data = Packet::packEncode($data, MainCmd::CMD_SYS, $scmd);
    $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
}

function pushDataToAll($room_no, $data, $scmd)
{
    $data = Packet::packFormat('OK', 1, $data);
    $return_data = Packet::packEncode($data, MainCmd::CMD_SYS, $scmd);
    $fds = getOtherFdsByRoomNo($room_no);
    $serv = server();
    foreach ($fds as $v) {
        $serv->push($v, $return_data, WEBSOCKET_OPCODE_BINARY);
    }
}

function pushErr($serv, $fd, $scmd, $msg)
{
    $data = Packet::packFormat('ERROR', 0, $msg);
    $return_data = Packet::packEncode($data, MainCmd::CMD_SYS, $scmd);
    $serv->push($fd, $return_data, WEBSOCKET_OPCODE_BINARY);
}

function pushErrToAll($room_no, $scmd, $msg)
{
    $data = Packet::packFormat('ERROR', 0, $msg);
    $return_data = Packet::packEncode($data, MainCmd::CMD_SYS, $scmd);
    $fds = getOtherFdsByRoomNo($room_no);
    $serv = server();
    foreach ($fds as $v) {
        $serv->push($v, $return_data, WEBSOCKET_OPCODE_BINARY);
    }
}

//延时广播
function broadCast(int $room_no, array $data, int $scmd, int $wait_time = 0)
{
//    $time = $wait_time * 1000;//时间转换为毫秒
    if ($wait_time) {
        sleep($wait_time);
    }
//    ddNode('do broadCast data: ' . json_encode($data, true));
    $serv = server();

    $waters = getRoomWait($room_no);
    foreach ($waters as $account) {
        $is_robot = is_robot($room_no, $account);
        $plstatus = playerStatus($room_no, $account);
        if ($plstatus == GameProgress::USER_ONLINE && !$is_robot) { //玩家在线才能发送消息
            $fd = getFdByAccount($account);
            pushDataToUser($serv, $fd, $data, $scmd);
        }
    }

}


//延时广播待定时器
function delayBroadCast($wait_time, int $room_no, array $data, int $scmd, int $gameTimer = 0)
{
//    ddNode('do delayBroadCast data: ' . json_encode($data, true));
    if (!$wait_time) { //不延时
        $serv = server();
        $ptype = getGameProgressType($room_no);
        if ($ptype) { //单机，只需要玩家一人发送消息即可
            $waters = getRoomWait($room_no);
            foreach ($waters as $account) {
                $is_robot = is_robot($room_no, $account);
                $plstatus = playerStatus($room_no, $account);
                if ($plstatus == GameProgress::USER_ONLINE && !$is_robot) { //玩家在线才能发送消息
                    $fd = getFdByAccount($account);
                    pushDataToUser($serv, $fd, $data, $scmd);
                }
            }
        } else { //多人
            $fds = getOtherFdsByRoomNo($room_no);
            foreach ($fds as $v) {
                $account = getAccountByFd($v);
                $plstatus = playerStatus($room_no, $account);
                if ($plstatus == GameProgress::USER_ONLINE) { //玩家在线才能发送消息
//                    $serv->push($v, $return_data, WEBSOCKET_OPCODE_BINARY);
                    pushDataToUser($serv, $v, $data, $scmd);
                }
            }
        }
        //发送消息为结算的时候清空准备数据
        if ($scmd == SubCmd::SUB_GAME_CACULAR) {
            //清除准备数据
            resetGamePrepare($room_no);
        }
    } else {
        $time = $wait_time * 1000;//时间转换为毫秒
        \Swoole\Timer::after((int)$time, function () use ($room_no, $data, $scmd) {
            $serv = server();
//            ddNode('do delayBroadCast data: ' . json_encode($data, true));
            $ptype = getGameProgressType($room_no);
            if ($ptype) { //单机，只需要玩家一人发送消息即可
                $waters = getRoomWait($room_no);
                foreach ($waters as $account) {
                    $is_robot = is_robot($room_no, $account);
                    $plstatus = playerStatus($room_no, $account);
                    if ($plstatus == GameProgress::USER_ONLINE && !$is_robot) { //玩家在线才能发送消息
                        $fd = getFdByAccount($account);
                        pushDataToUser($serv, $fd, $data, $scmd);
                    }
                }
            } else { //多人
                $fds = getOtherFdsByRoomNo($room_no);
                foreach ($fds as $v) {
                    $account = getAccountByFd($v);
                    $plstatus = playerStatus($room_no, $account);
                    if ($plstatus == GameProgress::USER_ONLINE) { //玩家在线才能发送消息
//                        $serv->push($v, $return_data, WEBSOCKET_OPCODE_BINARY);
                        pushDataToUser($serv, $v, $data, $scmd);
                    }
                }
            }
            //发送消息为结算的时候清空准备数据
            if ($scmd == SubCmd::SUB_GAME_CACULAR) {
                //清除准备数据
                resetGamePrepare($room_no);
            }
        });
    }

    if (!$gameTimer) {
        return true;
    }
    //添加定时器队列
    $user_conf = config('user');
    $name = '';
    switch ($gameTimer) {
        case \App\Game\Timer\GameTimer::$CHANGE_CARD_TIMER:
            $wait_time += $user_conf['change_timer'];
            $name = 'change_card';
            break;
        case \App\Game\Timer\GameTimer::$CHOOSE_COLOR_TIMER:
            $wait_time += $user_conf['choose_timer'];
            $name = 'choose_color';
            break;
        case \App\Game\Timer\GameTimer::$OUT_CARD_TIMER:
            $wait_time += $user_conf['play_timer'];
            $name = 'out_card';
            break;
        case \App\Game\Timer\GameTimer::$CHECK_HPK_TIMER:
            $wait_time += $user_conf['check_hpg_timer'];
            $name = 'check_hpg';
            break;
    }
    putTimerAdd($gameTimer, $room_no, $wait_time);
}


function removePGcards($cards, $peng, $gang)
{
//    dd('gang', $gang);
//    dd('peng', $peng);
    $restcards = array_diff($cards, $gang);
//    dd('restcards', $restcards);
    $res = array_diff($restcards, $peng);
//    dd('res', $res);
    return $res;
}

function combinePGcards($cards, $peng, $gang)
{
    $penArr = array_diff($peng, $gang);
    if (!empty($gang)) {
        foreach ($gang as $v) {
            $cards[] = [$v, $v, $v, $v];
        }
    }
    if (!empty($penArr)) {
        foreach ($penArr as $v) {
            $cards[] = [$v, $v, $v];
        }
    }
    return $cards;
}

function filterNickname($chars, $encoding = 'utf8')
{
    $pattern = ($encoding == 'utf8') ? '/[\x{4e00}-\x{9fa5}a-zA-Z0-9]/u' : '/[\x80-\xFF]/';
    preg_match_all($pattern, $chars, $result);
    return join('', $result[0]);

//    $str = preg_replace_callback('/./u',
//        function (array $match) {
//            return strlen($match[0]) >= 4 ? '' : $match[0];
//        },
//        $chars);
//    return $str;
}


function getRandomTingPaiCards($cards_pool)
{
//    dd('cards_pool', $cards_pool);
    //1.随机定缺
    $color = rand(1, 3);

    $colorArr = [
        1 => [1, 2, 3, 4, 5, 6, 7, 8, 9],
        2 => [11, 12, 13, 14, 15, 16, 17, 18, 19],
        3 => [21, 22, 23, 24, 25, 26, 27, 28, 29],
    ];
    //剔除缺牌，生成安全牌池
//    $safe_cards_pool = array_diff($cards_pool, $colorArr[$color]);
    $diff_arr = [];
    foreach ($cards_pool as $k => $v) {
        if (in_array($v, $colorArr[$color])) {
            $diff_arr[] = $v;
            unset($cards_pool[$k]);
        }
    }
//    dd('diff_arr', $diff_arr);
    $safe_cards_pool = $cards_pool;
    sort($safe_cards_pool);
    $count_values = array_count_values($safe_cards_pool);
    //2.随机给个将
    $jiang = array_rand($count_values, 1);
    foreach ($count_values as $card => $num) {
        if ($card == $jiang) {
            $count_values[$card] -= 2; //减去牌数
            if ($count_values[$card] == 0) {
                unset($count_values[$card]);
            }
            break;
        }
    }
    $hand_arr = [];//玩家手牌
    $shun1 = [];
    //3.发三组牌(顺子或者刻子,顺子不能和刻子有重复,最多两组刻子)
    //找顺子
    foreach ($count_values as $card => $num) {
        if ($card && isset($count_values[$card + 1]) && isset($count_values[$card + 2])) {
            $shun1 = [$card, $card + 1, $card + 2];
            $count_values[$card] -= 1; //减去牌数
            $count_values[$card + 1] -= 1; //减去牌数
            $count_values[$card + 2] -= 1; //减去牌数
            if ($count_values[$card] == 0) {
                unset($count_values[$card]);
            }
            if ($count_values[$card + 1] == 0) {
                unset($count_values[$card + 1]);
            }
            if ($count_values[$card + 2] == 0) {
                unset($count_values[$card + 2]);
            }
            break;
        }
    }
//    dd('shun1', $shun1);
    $ke1 = [];
    //找刻子
    foreach ($count_values as $card => $num) {
        if ($num > 2) {
            if (!in_array($card, $shun1)) {
                $ke1 = [$card, $card, $card];
                $count_values[$card] -= 3; //减去将牌数
                if ($count_values[$card] == 0) {
                    unset($count_values[$card]);
                }
                break;
            }
        }
    }
//    dd('ke1', $ke1);
    //找顺子
    $shun2 = [];
    foreach ($count_values as $card => $num) {
        if (!in_array($card, $shun1) && !in_array($card, $ke1)) {
            if ($card && isset($count_values[$card + 1]) && isset($count_values[$card + 2])) {
                $shun2 = [$card, $card + 1, $card + 2];
                $count_values[$card] -= 1; //减去牌数
                $count_values[$card + 1] -= 1; //减去牌数
                $count_values[$card + 2] -= 1; //减去牌数
                if ($count_values[$card] == 0) {
                    unset($count_values[$card]);
                }
                if ($count_values[$card + 1] == 0) {
                    unset($count_values[$card + 1]);
                }
                if ($count_values[$card + 2] == 0) {
                    unset($count_values[$card + 2]);
                }
                break;
            }
        }
    }
//    dd('shun2', $shun2);
    $zuheNum = 0;//组合牌数量（顺子，刻子）
    if (count($shun1)) {
        $hand_arr = array_merge($hand_arr, $shun1);
        $zuheNum += 1; //每有一组，则组合牌数量
    }
    if (count($ke1)) {
        $hand_arr = array_merge($hand_arr, $ke1);
        $zuheNum += 1; //每有一组，则组合牌数量
    }
    if (count($shun2)) {
        $hand_arr = array_merge($hand_arr, $shun2);
        $zuheNum += 1; //每有一组，则组合牌数量
    }
    $ke2 = [];
    if ($zuheNum < 3) { //组合数小于3则继续再找一组
        //找刻子
        foreach ($count_values as $card => $num) {
            if ($num > 2) {
                if (!in_array($card, $shun1) && !in_array($card, $ke1) && !in_array($card, $shun2)) {
                    $ke2 = [$card, $card, $card];
                    $count_values[$card] -= 3; //减去将牌数
                    if ($count_values[$card] == 0) {
                        unset($count_values[$card]);
                    }
                    break;
                }
            }
        }
    }
//    dd('ke2', $ke2);
    if (count($ke2)) {
        $hand_arr = array_merge($hand_arr, $ke2);
        $zuheNum += 1; //每有一组，则组合牌数量
    }
    //组合数还小于3则随机分配牌
    $rest_cars = [];
    foreach ($count_values as $card => $num) {
        for ($i = 0; $i < $num; $i++) {
            $rest_cars[] = $card;
        }
    }
//    dd('rest_cars', $rest_cars);
    if ($zuheNum < 3) {
        $temarr = [];
        for ($i = 0; $i < 3 - $zuheNum; $i++) {
            $arr = array_rand($rest_cars, 3);
            $temarr = array_merge($temarr, $arr);
            $hand_arr[] = $arr;
        }
        foreach ($temarr as $v) {
            foreach ($rest_cars as $k => $m) {
                if ($v == $m) {
                    unset($rest_cars[$k]);
                    continue;
                }
            }
        }
    }
//    dd('hand_arr', $hand_arr);
    $dui = [];
    //4.发个对子或者间隔在2以内的单牌(单牌不能有刻子)
    $new_count_values = array_count_values($rest_cars);
//    dd('new_count_values', $new_count_values);
    foreach ($new_count_values as $card => $num) {
        if ($num > 1 && $card != $jiang) {
            if (!in_array($card, $shun1) && !in_array($card, $ke1) && !in_array($card, $shun2) &&
                !in_array($card, $ke2)) {
                $dui = [$card, $card];
                $new_count_values[$card] -= 2;
                if ($new_count_values[$card] == 0) {
                    unset($new_count_values[$card]);
                }
                break;
            }
        }
    }
//    dd('new_count_values', $new_count_values);
//    dd('dui', $dui);
    $new_rest_cars = [];
    foreach ($new_count_values as $card => $num) {
        for ($i = 0; $i < $num; $i++) {
            $new_rest_cars[] = $card;
        }
    }
//    dd('new_rest_cars', $new_rest_cars);
    if (!count($dui)) {
        $arr = array_rand($new_rest_cars, 2);
        $dui = array_merge($dui, $arr);
        foreach ($arr as $v) {
            foreach ($new_rest_cars as $k => $m) {
                if ($v == $m) {
                    unset($new_rest_cars[$k]);
                    continue;
                }
            }
        }
    }
//    dd('dui', $dui);
    $new_rest_cars = array_merge($new_rest_cars, $diff_arr);
    sort($new_rest_cars);
    $usercards = array_merge($hand_arr, $dui, [$jiang, $jiang]);
    $data = ['color' => $color, 'cards' => $usercards, 'rest_cards' => $new_rest_cars];
//    dd('data', $data);
    return $data;
}

function removeRoomMapByAccount($account)
{
    $game_conf = config('game');
    $room_map_conf = $game_conf['user_room'];
    $room_map_key = sprintf($room_map_conf, $account);
    redis()->del($room_map_key);// 删除绑定用户房间号
}

function addRoomUserColor($room_no, $chair_id, $color)
{
    $game_conf = config('game');
    $room_color_key = sprintf($game_conf['user_room_color'], $room_no);
    redis()->hSet($room_color_key, (string)$chair_id, $color);
}

/**
 * 更新积分榜
 * @param $log_data
 * @throws \Exception
 */
function resetRankSort($log_data)
{
    $score_conf = config('score');
    $rank_key = $score_conf['score_rank'];
    $rank_day_key = $score_conf['score_rank_day'];
    foreach ($log_data as $k => $v) {
        $account = $v['user_id'];
        $score = (int)$v['score'];
        $symbol = $v['symbol'];
//        if ($symbol == '-') {
//            $score = $score;
//        }
        $is_limit = checkScoreLimit($account);
        if (!$is_limit && ($v['is_robot'] == 0)) {
            if (redis()->exists($rank_key)) {
                redis()->zIncrBy($rank_key, (float)$score, (string)$account);
            } else {
                $date = new \DateTime();//时间对象
                $date->modify("this week +6 days");//该周的最后一天
                $end_time = $date->format('Y-m-d') . ' 23:59:59';
                //得到本周最后一天的时间戳
                $expire = strtotime($end_time) - time();
                redis()->zAdd($rank_key, (float)$score, (string)$account);
                redis()->expire($rank_key, $expire);
            }
            if (redis()->exists($rank_day_key)) {
                redis()->zIncrBy($rank_day_key, (float)$score, (string)$account);
            } else {
                $end_day = date('Y-m-d', time()) . ' 23:59:59';
                $end_time = strtotime($end_day) - time();
                redis()->zAdd($rank_day_key, (float)$score, (string)$account);
                redis()->expire($rank_day_key, $end_time);
            }
        }
    }
}

function pushForceQuitMsg($room_no, $account)
{
    $msg = ['status' => '强制退出'];
    $serv = server();
    $fd = getFdByAccount($account);
    pushDataToUser($serv, $fd, $msg, SubCmd::FORCE_QUIT_ROOM_RESP);
    $msg = ['status' => '退出房间', 'chair_id' => getChairIdbyAccount($room_no, $account)];
    broadCast($room_no, $msg, SubCmd::SUB_GAME_ROOM_QUIT_RESP);
    $num = 1;
    $waiters = getRoomWait($room_no);
    if (!empty($waiters)) {
        $num = count($waiters);
    } else {//没有等待玩家，则直接清空房间数据
        clearRoomDataAll($room_no);
    }
    //清空该玩家数据
    removeRoomMapByAccount($account);
    removeRoomWaitByAccount($room_no, $account);
    removeGamePrepareByAccount($room_no, $account);
    removeRoomUserDataByAccount($room_no, $account);
    removeRoomChairByAccount($room_no, $account);
    $num -= 1; //去掉退出的玩家
    if (!$num) { //如果没有玩家了，则清空房间数据
        clearRoomDataAll($room_no);
    }
}


//根据memo获取对应的type类型
function getScoreLogTypeByMemo($memo)
{
    $type = 0; //明细类型

    $typeArr = [
        '麻将游戏' => 7,
        '麻将游戏累计签到' => 8,
        '麻将游戏签到' => 9,
        '购买猜拳机会' => 10,
        '猜拳游戏' => 11,
        '猜拳游戏等级提升所得' => 12,
        '红包游戏' => 13,
        '三消游戏' => 14,
        '斗地主' => 15,
        '斗地主阶段签到' => 16,
        '斗地主签到' => 17,
        '任务中心-每日任务' => 18,
        '任务中心-成长任务' => 19,
        '任务中心-活跃宝箱' => 20,
        '盲盒-抽奖' => 21,
        '摇钱树升至等级1' => 22,
        '摇钱树升至等级2' => 23,
        '摇钱树升至等级3' => 24,
        '摇钱树升至等级4' => 25,
        '摇钱树升至等级5' => 26,
    ];

    if (isset($typeArr[$memo])) {
        $type = $typeArr[$memo];
    }
    return $type;
}